#!@PYTHON@
""" Timing comparisons for crm_simulate profiling output
"""

__copyright__ = "Copyright 2019-2023 the Pacemaker project contributors"
__license__ = "GNU General Public License version 2 or later (GPLv2+) WITHOUT ANY WARRANTY"

import io
import re
import sys
import errno
import argparse
import os

# These imports allow running from a source checkout after running `make`.
# Note that while this doesn't necessarily mean it will successfully run tests,
# but being able to see --help output can be useful.
if os.path.exists("@abs_top_srcdir@/python"):
    sys.path.insert(0, "@abs_top_srcdir@/python")

if os.path.exists("@abs_top_builddir@/python") and "@abs_top_builddir@" != "@abs_top_srcdir@":
    sys.path.insert(0, "@abs_top_builddir@/python")

from pacemaker.exitstatus import ExitStatus

DESC = """Compare timings from crm_simulate profiling output"""

BEFORE_HELP = """Output of "crm_simulate --profile cts/scheduler --repeat <N>" from earlier Pacemaker build"""

# line like: * Testing cts/scheduler/xml/1360.xml ... 0.07 secs
PATTERN = r"""^\s*\*\s+Testing\s+.*/([^/]+)\.xml\s+\.+\s+([.0-9]+)\s+secs\s*$"""

def parse_args(argv=sys.argv):
    """ Parse command-line arguments """

    parser = argparse.ArgumentParser(description=DESC)

    parser.add_argument('-V', '--verbose', action='count',
                        help='Increase verbosity')

    parser.add_argument('-p', '--threshold-percent', type=float, default=0,
                        help="Don't show tests with less than this percentage difference in times")

    parser.add_argument('-s', '--threshold-seconds', type=float, default=0,
                        help="Don't show tests with less than this seconds difference in times")

    parser.add_argument('-S', '--sort', choices=['test', 'before', 'after', 'diff', 'percent'],
                        default='test', help="Sort results by this column")

    parser.add_argument('-r', '--reverse', action='store_true',
                        help="Sort results in descending order")

    parser.add_argument('before_file', metavar='BEFORE',
                        type=argparse.FileType('r'),
                        help=BEFORE_HELP)

    parser.add_argument('after_file', metavar='AFTER',
                        type=argparse.FileType('r'),
                        help='Output of same command from later Pacemaker build')

    return parser.parse_args(argv[1:])


def extract_times(infile):
    """ Extract test names and times into hash table from file """

    result = {}
    for line in infile:
        match = re.search(PATTERN, line)
        if match is not None:
            result[match.group(1)] = match.group(2)
    return result


def compare_test(test, before, after, args):
    """ Compare one test's timings """

    try:
        before_time = float(before[test])
    except KeyError:
        if args.verbose > 0:
            print("No previous test " + test + " to compare")
        return None

    after_time = float(after[test])

    time_diff = after_time - before_time
    time_diff_percent = (time_diff / before_time) * 100

    if ((abs(time_diff) >= args.threshold_seconds)
        and (abs(time_diff_percent) >= args.threshold_percent)):
        return { 'test': test,
                 'before': before_time,
                 'after': after_time,
                 'diff': time_diff,
                 'percent': time_diff_percent
               }
    return None

def sort_diff(result):
    """ Sort two test results by time difference """

    global sort_field

    return result[sort_field]


def print_results(results, sort_reverse):
    """ Output the comparison results """

    if results == []:
        return

    # Sort and print test differences
    results.sort(reverse=sort_reverse, key=sort_diff)
    for result in results:
        print("%-40s %6.2fs vs %6.2fs (%+.2fs = %+6.2f%%)" % (result['test'],
              result['before'], result['after'], result['diff'],
              result['percent']))

    # Print average differences
    diff_total = sum(d['diff'] for d in results)
    percent_total = sum(d['percent'] for d in results)
    nresults = len(results)
    print("\nAverages: %+.2fs %+6.2f%%" % ((diff_total / nresults),
          (percent_total / nresults)))


if __name__ == "__main__":

    global sort_field

    try:
        args = parse_args()
        if args.verbose is None:
            args.verbose = 0

        before = extract_times(args.before_file)
        after = extract_times(args.after_file)
        sort_field = args.sort

        # Build a list of test differences
        results = []
        for test in after.keys():
            result = compare_test(test, before, after, args)
            if result is not None:
                results = results + [ result ]

        print_results(results, sort_reverse=args.reverse)

    except KeyboardInterrupt:
        pass
    except IOError as e:
        if e.errno != errno.EPIPE:
            raise

    sys.exit(ExitStatus.OK)

# vim: set filetype=python expandtab tabstop=4 softtabstop=4 shiftwidth=4 textwidth=120:
